<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		
		 input 1<input type="text" id="input1" />
		input 2<input type="text" id="input2" />
		
		
		<script>
			
			/*
			1.什么是防抖？
			防抖就是防止在一段时间内客户端向服务器频繁发送多个相同的请求,例如我们搜索框监听用户输入事件然后向后端发起请求,
			但是在用户输入的过程内会这个监听事件会不断产生,然后向服务器发送很多个请求会对服务器造成很大压力,
			所以我们可以使用防抖处理也就是只对服务器发送一次请求,就是当用户在一段时间内输入完毕时对服务器发送请求
			2.怎么解决？
			定义一个变量val存储定时器，做防抖处理最关键的一步就是利用定时器做，
			利用定时器隔一段时间才向服务器发送请求，当在定时器延迟的这段时间内用户还在继续输入时,就清除前面的定时器重新给计时器赋值使他重新计时，
			当用户最后一次输入时清楚前面的定时器后再对定时器重新赋值使定时器重新计时,
			只要用户再定时器延迟的这段时间内不再输入就不会清除前面的定时器,定时器也就不会被重新赋值然后计时
			*/
		   
		   //第一种防抖处理(最简单但是不推荐使用)
			//首先拿到input输入框
			let inp1=document.querySelector('#input1');
			
			//定义定时器变量存储定时器
			var val=1;
			//然后监听此输入框的输入事件,每当用户输入时就会调用该方法
			inp1.oninput=function(){
				/*当用户输入时该方法一调用发现之前还有定时器并且还没执行，就清除之前的定时器,然后再重新给该定时器赋值使他重新计时，
				只要用户不再次输入,也就不会调用该方法,也就不会清除之前的定时器重新计时,所以用户最后一次调用该方法时,也就是定时器最后一次重新计时,
				只要等定时器计时过后我们写在定时器中的方法或者请求就会被执行
				*/
				if(val!=null){
					clearTimeout(val)
					console.log("我执行了")
				}
				val=setTimeout(()=>{					
					//定时器内部这里放逻辑代码
					console.log(this)//this指向上一层的this，这里上一层的this指向的是调用该方法的对象，也就是id为input1的标签
					console.log("我像服务器发送请求了啊")
				},1000)
				console.log("我也执行了")			
			 }
			 
			 
			 
			/*---------------------------------------------------------------------------------------------------------------------------------------*/ 
			 // 第二种防抖处理(推荐但写起来较为复杂)
			 /*
			 在第一种防抖方法基础上做一层封装，通过封装闭包和回调函数的形式来自己定义一个方法在该方法中处理逻辑大大提高了代码的可读性和可维护性
			 上面这种方法已经满足了正常的防抖处理，但是在企业中你把逻辑代码写在定时器内部如果一大堆就会严重降低代码的可读性和维护性,
			 所以我们如果可以自己定义一个方法在方法内部写逻辑代码然后在定时器内部执行该方法就行了
			 */
			
			//第一步照样先获取到需要做防抖处理的input标签
			let inp2=document.querySelector('#input2')
	
			
	
			 /*-------------------------------------------------------------------------------
			 
			 第二步照样监听input的输入事件,注意oninput事件对应一个方法,所以我们这里直接自调用debounce函数返回给oninput事件一个方法,
			 所以每次触发输入事件时最终执行的都是debounce内部返回的那个函数
			 
			 目   的：
			 为了用户能够直接在监听输入事件的方法那个地方直接书写逻辑，因为代码就这样一目了然，而且很方便，。
			 所以我们封装了一个debounce方法内部返回一个函数作为监听输入事件的方法，
			 在定义debounce函数中利用回调函数作为他的第一个参数,第二个参数作为定时器的延迟时间
			 目的使我们能够直接在回调函数中直接写逻辑相关代码而不用管内部封装过程
			 
			 这里debouce()直接就自调用了，在页面初始化就调用了，
			 调用之后debouce方法之后debouce方法就把内部返回的方法赋值给了inp2的监听事件，
			 所以之后每次触发输入事件时执行的都是返回的这个方法
			 ----------------------------------------------------------------------------------*/
			
			 inp2.oninput=debounce(function(){
				 //改变该回调函数this的指向前console.log(this)打印结果为window
				 /*----------------------------------------------------------
				 这样我们就可以在这个回调函数中直接处理需要防抖的代码逻辑就行了,
				 但是我们一般需要拿到该输入框的value值，这怎么拿呢,通过this吗,这个回调函数是在定时器内部自调用的，所以内部this指向window，
				 所以嘿嘿，我们是不是只要改变这个回调函数内部的this指向到这个标签对象不就可以通过this拿到该标签的value值了吗，
				 所以我们在调用这个回调函数的地方使用call改变其this指向的值
				----------------------------------------------------------- */
				 console.log(`我要把input2的value值:${this.value},发送到服务器`)
			 
			 
			 },1000)
			 
			
			
			//定义debouce方法
			function debounce (fn,delay){	
			
				/*---------------------------------------------------------------------------------
				容易混淆点！！！！！！！！！！！！！！！！！！！！！
				上面debouce()就直接自调用了，在页面初始化执行js代码的时候就调用了,
				调用之后就把内部返回的方法赋值给了inp2的输入事件,所以之后每次触发inp2的输入事件调用的都是debouce内部返回的函数
				所以debounce内部的代码只会执行一次也就是val2不会被反复赋值为null,
				之所以会误解val2被反复赋值为null主要是认为每次触发监听事件执行的都是debouce方法,其实这是错误的,每次执行的应该是debouce执行后内部返回的方法
				而debouce()是直接自调用的也就是在页面执行js脚本时就直接调用了
				------------------------------------------------------------------------------------*/
			
				let val2=null;//这一步声明一个变量在保存定时器，		   
				console.log(this)//这里打印this,因为debounce在上面直接就子调用了没有调用对象所以指向this
				return function(){
					if(val2!=null){
						clearTimeout(val2)
					}
					val2=setTimeout(()=>{
						console.log(this)
						/*
						这个箭头函数中的this指向上一层的this，上一层的this也就是这个被return出去的函数中的this，
						因为每次input2标签输入事件一触发就调用该函数所以该函数中的this肯定是指向id为inout2标签对象啦
						定时器内部这里放逻辑代码
						
						这个call()括号中的this是settimeout这个箭头回调函数中的this，
						然后这段代码的意思是将fn回调函数中的this指向call()括号中的this
						*/
						fn.call(this)
						//好了到这一步既改变了fn回调函数内部this最终指向为input2标签，使得fn回调函数内部可以通过this获得input2标签的value，又调用了该回调函数
						
					},delay)
				}
			}
			
			
		</script>
	</body>
</html>
